package com.aizuda.boot.modules.business.feishu;

import com.aizuda.boot.config.properties.FeiShuConfigProperties;
import com.aizuda.boot.modules.auth.domain.entity.ThirdUser;
import com.aizuda.boot.modules.auth.domain.entity.ThirdUserAuth;
import com.aizuda.boot.modules.auth.service.ThirdUserAuthService;
import com.aizuda.boot.modules.auth.service.ThirdUserService;
import com.aizuda.boot.modules.business.contract.dto.UserInfo;
import com.aizuda.boot.modules.business.contract.util.AuthUtil;
import com.aizuda.boot.modules.business.feishu.domain.approval.CreateApprovalDTO;
import com.aizuda.boot.modules.business.feishu.domain.approval.SendFeiShuBotDTO;
import com.aizuda.boot.modules.business.feishu.domain.approval.SynchronizationApprovalDTO;
import com.aizuda.boot.modules.business.feishu.domain.template.*;
import com.aizuda.boot.modules.business.feishu.strategy.FeishuConfigStrategy;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.gson.JsonParser;
import com.lark.oapi.Client;
import com.lark.oapi.core.utils.Jsons;
import com.lark.oapi.service.approval.v4.model.*;
import com.lark.oapi.service.approval.v4.model.I18nResource;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.utils.URLEncodedUtils;
import org.jetbrains.annotations.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @Author sangyirong
 * @ClassName FeiShuApproval
 * @Version 1.0
 * @Date 2025/7/17 13:57
 */
@Slf4j
@Component
public class FeiShuApproval {

    @Resource
    private FeiShuConfigProperties feiShuConfigProperties;

    @Resource
    private FeishuConfigStrategy feishuConfigStrategy;

    @Resource
    private ThirdUserService thirdUserService;

    @Resource
    private ThirdUserAuthService thirdUserAuthService;

    /**
     * 获取飞书客户端
     *
     * @return
     */
    public Client getClient(String tenantKey) {
        FeiShuConfigProperties.FeiShuAppConfig config = feishuConfigStrategy.getConfigByTenantKey(tenantKey);
        if (null == config) {
            return null;
        }
        return Client.newBuilder(config.getAppId(), config.getAppSecret()).build();
    }

    /**
     * 创建审批定义
     *
     * @param dto
     * @return
     * @throws Exception
     */
    public String createApproval(CreateApprovalDTO dto) throws Exception {
        Client client = getClient(dto.getTenantKey());

        // 创建请求对象
        CreateExternalApprovalReq req = CreateExternalApprovalReq.newBuilder()
                .departmentIdType("open_department_id")
                .userIdType("open_id")
                .externalApproval(ExternalApproval.newBuilder()
                        .approvalName("@i18n@approvalName")
                        .approvalCode(dto.getApprovalCode())
                        .groupCode(dto.getGroupCode())
                        .groupName("@i18n@groupName")
                        .description("@i18n@desc")
                        .external(ApprovalCreateExternal.newBuilder()
                                .bizType(dto.getBizType())
                                // 回调地址
                                .actionCallbackUrl(feiShuConfigProperties.getActionCallbackUrl())
                                .build())
                        .viewers(new ApprovalCreateViewers[]{
                                ApprovalCreateViewers.newBuilder()
                                        .viewerType("NONE")
                                        .build()
                        })
                        .i18nResources(new I18nResource[]{
                                I18nResource.newBuilder()
                                        .locale("zh-CN")
                                        .texts(new I18nResourceText[]{
                                                I18nResourceText.newBuilder()
                                                        .key("@i18n@approvalName")
                                                        .value(dto.getApprovalName())
                                                        .build(),
                                                I18nResourceText.newBuilder()
                                                        .key("@i18n@groupName")
                                                        .value(dto.getGroupName())
                                                        .build(),
                                                I18nResourceText.newBuilder()
                                                        .key("@i18n@desc")
                                                        .value(null == dto.getDescription() ? "暂无描述。" : dto.getDescription())
                                                        .build()
                                        })
                                        .isDefault(true)
                                        .build()
                        })
                        .managers(null == dto.getOpenId() ? null : dto.getOpenId().toArray(new String[0]))
                        .build())
                .build();

        // 发起请求
        CreateExternalApprovalResp resp = client.approval().v4().externalApproval().create(req);

        // 处理服务端错误
        if (!resp.success()) {
            log.error("code:{},msg:{},reqId:{}, resp:{}", resp.getCode(), resp.getMsg(), resp.getRequestId(), Jsons.createGSON(true, false).toJson(JsonParser.parseString(new String(resp.getRawResponse().getBody(), StandardCharsets.UTF_8))));
            return null;
        }

        // 业务数据处理
        log.info("创建审批定义成功{}", Jsons.DEFAULT.toJson(resp.getData()));
        return resp.getData().getApprovalCode();
    }

    /**
     * 获取审批定义
     *
     * @param approvalCode 飞书审批定义code
     * @return
     * @throws Exception
     */
    public GetExternalApprovalRespBody getApproval(String approvalCode, String tenantKey) throws Exception {
        // 构建client
        Client client = getClient(tenantKey);

        // 创建请求对象
        GetExternalApprovalReq req = GetExternalApprovalReq.newBuilder()
                .approvalCode(approvalCode)
                .build();

        // 发起请求
        GetExternalApprovalResp resp = client.approval().v4().externalApproval().get(req);

        // 处理服务端错误
        if (!resp.success()) {
            System.out.println(String.format("code:%s,msg:%s,reqId:%s, resp:%s",
                    resp.getCode(), resp.getMsg(), resp.getRequestId(), Jsons.createGSON(true, false).toJson(JsonParser.parseString(new String(resp.getRawResponse().getBody(), StandardCharsets.UTF_8)))));
            return null;
        }

        // 业务数据处理
        log.info(Jsons.DEFAULT.toJson(resp.getData()));
        return resp.getData();
    }

    /**
     * 同步审批
     */
    public void synchronizationApproval(SynchronizationApprovalDTO dto) throws Exception {
        Client client = getClient(dto.getTenantKey());
        List<ExternalInstanceForm> formList = new ArrayList<>();
        ArrayList<ExternalInstanceTaskNode> taskList = new ArrayList<>();
        ArrayList<CcNode> ccList = new ArrayList<>();
        List<I18nResourceText> i18nList = new ArrayList<>();
        // 表单
        if (!CollectionUtils.isEmpty(dto.getForms())) {
            for (int i = 0; i < dto.getForms().size(); i++) {
                ExternalInstanceForm build = ExternalInstanceForm.newBuilder()
                        .name("@i18n@name" + (i + 1))
                        .value("@i18n@value" + (i + 1))
                        .build();
                formList.add(build);
                I18nResourceText nameI18n = I18nResourceText.newBuilder()
                        .key("@i18n@name" + (i + 1))
                        .value(dto.getForms().get(i).getName())
                        .build();
                i18nList.add(nameI18n);
                I18nResourceText valueI18n = I18nResourceText.newBuilder()
                        .key("@i18n@value" + (i + 1))
                        .value(dto.getForms().get(i).getValue())
                        .build();
                i18nList.add(valueI18n);
            }
        }

        // 任务
        if (!CollectionUtils.isEmpty(dto.getTasks())) {
            for (int i = 0; i < dto.getTasks().size(); i++) {
                SynchronizationApprovalDTO.Task task = dto.getTasks().get(i);
                String openId = getOpenId(task.getTaskUserId(), dto.getTenantKey());
                ExternalInstanceTaskNode taskNode = ExternalInstanceTaskNode.newBuilder()
                        .taskId(task.getTaskId())
                        .openId(openId)
                        .userId(openId == null ? task.getTaskUserId() : null)
                        .title("@i18n@taskTitle")
                        .links(ExternalInstanceLink.newBuilder()
                                .pcLink(feiShuConfigProperties.getDefaultRedirectUri() + "/contract/all-contracts?id=" + dto.getContractId())
                                .mobileLink(feiShuConfigProperties.getDefaultRedirectUri() + "/contract/all-contracts?id=" + dto.getContractId())
                                .build())
                        .status(task.getTaskStatus())
                        .extra(task.getExtra())
                        .createTime(getTimestamp(task.getStartTime()))
                        .endTime(null == task.getEndTime() ? "0" : getTimestamp(task.getEndTime()))
                        .updateTime(getTimestamp(task.getUpdateTime()))
                        .actionContext(task.getTaskId())
                        // 快捷审批目前仅支持在飞书移动端操作。
                        .actionConfigs(new ActionConfig[]{
                                ActionConfig.newBuilder()
                                        .actionType("APPROVE")
                                        .isNeedReason(false)
                                        .isReasonRequired(false)
                                        .isNeedAttachment(false)
                                        .build(),
                                ActionConfig.newBuilder()
                                        .actionType("REJECT")
                                        .isNeedReason(true)
                                        .isReasonRequired(true)
                                        .isNeedAttachment(false)
                                        .build()
                        })
                        .displayMethod("NORMAL")
                        .excludeStatistics(false)
                        .nodeId(task.getNodeId())
                        .nodeName("@i18n@node" + (i + 1))
                        .build();
                taskList.add(taskNode);

                I18nResourceText nodeNameI18n = I18nResourceText.newBuilder()
                        .key("@i18n@node" + (i + 1))
                        .value(task.getNodeName() == null ? "未知" : task.getNodeName())
                        .build();
                i18nList.add(nodeNameI18n);
            }
        }

        // 抄送
        if (!CollectionUtils.isEmpty(dto.getCcTasks())) {
            for (int i = 0; i < dto.getCcTasks().size(); i++) {
                SynchronizationApprovalDTO.CcTask ccTask = dto.getCcTasks().get(i);
                CcNode ccNode = CcNode.newBuilder()
                        .ccId(ccTask.getCcId())
                        .openId(getOpenId(ccTask.getCcUserId(), dto.getTenantKey()))
                        .links(ExternalInstanceLink.newBuilder()
                                .pcLink(feiShuConfigProperties.getDefaultRedirectUri() + "/contract/all-contracts?id=" + dto.getContractId())
                                .mobileLink(feiShuConfigProperties.getDefaultRedirectUri() + "/contract/all-contracts?id=" + dto.getContractId())
                                .build())
                        .readStatus(ccTask.getReadStatus())
                        .title(ccTask.getTitle())
                        .createTime(getTimestamp(ccTask.getCreateTime()))
                        .updateTime(getTimestamp(ccTask.getUpdateTime()))
                        .displayMethod("NORMAL")
                        .build();
                ccList.add(ccNode);
            }
        }

        I18nResourceText titleI18n = I18nResourceText.newBuilder()
                .key("@i18n@taskTitle")
                .value(dto.getTitle() == null ? "未知" : dto.getTitle())
                .build();
        i18nList.add(titleI18n);

        i18nList.add(I18nResourceText.newBuilder()
                .key("@i18n@title")
                .value(dto.getTitle() == null ? "未知" : dto.getTitle())
                .build());

        String openId = getOpenId(dto.getApplyUserId(), dto.getTenantKey());
        // 创建请求对象
        CreateExternalInstanceReq req = CreateExternalInstanceReq.newBuilder()
                .externalInstance(ExternalInstance.newBuilder()
                        .approvalCode(dto.getApprovalCode())
                        .status(dto.getInstanceStatus())
                        .instanceId(dto.getInstanceId())
                        .links(ExternalInstanceLink.newBuilder()
                                .pcLink(feiShuConfigProperties.getDefaultRedirectUri() + "/contract/all-contracts?id=" + dto.getContractId())
                                .mobileLink(feiShuConfigProperties.getDefaultRedirectUri() + "/contract/all-contracts?id=" + dto.getContractId())
                                .build())
                        .title("@i18n@title")
                        .form(formList.toArray(new ExternalInstanceForm[0]))
                        .openId(openId)
                        .userId(openId == null ? dto.getApplyUserId() : null)
                        .startTime(getTimestamp(dto.getStartTime()))
                        .endTime(null == dto.getEndTime() ? "0" : getTimestamp(dto.getEndTime()))
                        .updateTime(getTimestamp(dto.getUpdateTime()))
                        .displayMethod("NORMAL")
                        .updateMode("UPDATE")
                        .taskList(taskList.toArray(new ExternalInstanceTaskNode[0]))
                        .ccList(ccList.toArray(new CcNode[0]))
                        .i18nResources(new I18nResource[]{
                                I18nResource.newBuilder()
                                        .locale("zh-CN")
                                        .texts(i18nList.toArray(new I18nResourceText[0]))
                                        .isDefault(true)
                                        .build()
                        })
                        .build())
                .build();

        // 发起请求
        CreateExternalInstanceResp resp = client.approval().v4().externalInstance().create(req);

        // 处理服务端错误
        if (!resp.success()) {
            log.error("code:{},msg:{},reqId:{}, resp:{}", resp.getCode(), resp.getMsg(), resp.getRequestId(), Jsons.createGSON(true, false).toJson(JsonParser.parseString(new String(resp.getRawResponse().getBody(), StandardCharsets.UTF_8))));
            return;
        }

        // 业务数据处理
        log.info("流程同步成功{}", Jsons.DEFAULT.toJson(resp.getData()));
    }


    /**
     * 发送飞书审批
     *
     * @throws Exception
     */
    public String sendApproval(SendFeiShuBotDTO dto) throws Exception {
        String accessToken = getAccessToken(dto.getTenantKey());
        if (null == accessToken) {
            log.error("获取飞书token失败");
            return null;
        }
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .header("Authorization", "Bearer " + accessToken)
                .header("Content-Type", "application/json; charset=utf-8")
                .uri(URI.create("https://open.feishu.cn/open-apis/approval/v1/message/send"))
                .POST(HttpRequest.BodyPublishers.ofString(getTemplate(dto)))
                .build();
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        JSONObject result = JSON.parseObject(response.body());
        log.info(JSON.toJSONString(result));
        if (result.getInteger("code") == 0) {
            log.info("创建审批成功：{}", result.getString("message_id"));
            return result.getObject("data", JSONObject.class).getString("message_id");
        }
        return null;
    }

    /**
     * 获取审批通用模板
     *
     * @return
     */
    private String getTemplate(SendFeiShuBotDTO dto) {
        String summary = "@i18n@summary";

        HashMap<String, String> i18 = new HashMap<>();
        List<Summaries> summaries = new ArrayList<>();
        if (!CollectionUtils.isEmpty(dto.getSummary())) {
            for (int i = 0; i < dto.getSummary().size(); i++) {
                summaries.add(Summaries.builder()
                        .summary(summary + i)
                        .build());
                i18.put(summary + i, dto.getSummary().get(i));
            }
        }
        if (null != dto.getApprovalName()) {
            i18.put("@i18n@approvalName", dto.getApprovalName());
        }

        String url = feiShuConfigProperties.getDefaultRedirectUri() + "/contract/all-contracts?id=" + dto.getContractId();
        String encodedUrl = URLEncoder.encode(url, StandardCharsets.UTF_8);

        ApprovalTemplate template = ApprovalTemplate.builder()
                .templateId(dto.getTemplateId())
                .openId(getOpenId(dto.getUserId(), dto.getTenantKey()))
                .uuid(UUID.randomUUID().toString())
                .approvalName("@i18n@approvalName")
                .titleUserId(getOpenId(dto.getApplyUserId(), dto.getTenantKey()))
                .titleUserIdType("open_id")
                .content(
                        Content.builder()
                                .userId(getOpenId(dto.getApplyUserId(), dto.getTenantKey()))
                                .userIdType("open_id")
                                .summaries(
                                        summaries.toArray(new Summaries[0])
                                )
                                .build()
                )
                .actions(
                        new Action[]{
                                Action.builder()
                                        .actionName("DETAIL")
                                        .url("https://applink.feishu.cn/client/web_app/open?appId=" + dto.getAppId() + "&lk_target_url=" + encodedUrl)
                                        .pcUrl("https://applink.feishu.cn/client/web_app/open?appId=" + dto.getAppId() + "&lk_target_url=" + encodedUrl)
                                        .androidUrl("https://applink.feishu.cn/client/web_app/open?appId=" + dto.getAppId() + "&lk_target_url=" + encodedUrl)
                                        .iosUrl("https://applink.feishu.cn/client/web_app/open?appId=" + dto.getAppId() + "&lk_target_url=" + encodedUrl)
                                        .build()
                        }
                )
                .actionCallback(
                        ActionCallback.builder()
                                .actionCallbackUrl(feiShuConfigProperties.getActionCallbackUrl())
                                .actionContext(dto.getContractId() + ":" + dto.getTaskId())
                                .actionCallbackToken(dto.getToken())
                                .build()
                )
                .actionConfigs(
                        new ActionConfigs[]{
                                ActionConfigs.builder()
                                        .actionType("APPROVE")
                                        .actionName("同意")
                                        .isNeedReason(false)
                                        .isReasonRequired(false)
                                        .isNeedAttachment(false)
                                        .nextStatus("APPROVED")
                                        .build(),
                                ActionConfigs.builder()
                                        .actionType("REJECT")
                                        .actionName("拒绝")
                                        .isNeedReason(true)
                                        .isReasonRequired(true)
                                        .isNeedAttachment(false)
                                        .nextStatus("REJECTED")
                                        .build()
                        })
                .i18nResources(
                        new I18nResources[]{
                                I18nResources.builder()
                                        .locale("zh-CN")
                                        .isDefault(true)
                                        .texts(i18)
                                        .build()

                        })
                .build();
        System.out.println(JSON.toJSONString(template));
        return JSON.toJSONString(template);
    }

    public String updateApproval(String tenantKey, String messageId, String actionType) throws Exception {
        String accessToken = getAccessToken(tenantKey);
        if (null == accessToken) {
            log.error("获取飞书token失败");
            return null;
        }
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest httpRequest = HttpRequest.newBuilder()
                .header("Authorization", "Bearer " + accessToken)
                .header("Content-Type", "application/json; charset=utf-8")
                .POST(HttpRequest.BodyPublishers.ofString(getUpdateTemplate(messageId, actionType)))
                .uri(URI.create("https://open.feishu.cn/open-apis/approval/v1/message/update"))
                .build();

        HttpResponse<String> response = client.send(httpRequest, HttpResponse.BodyHandlers.ofString());
        JSONObject result = JSON.parseObject(response.body());
        log.info(JSON.toJSONString(result));
        if (result.getInteger("code") == 0) {
            log.info("创建审批成功：{}", result.getString("message_id"));
            return result.getObject("data", JSONObject.class).getString("message_id");
        }
        return null;
    }

    public String getUpdateTemplate(String messageId, String actionType) {
        UpdateTemplate template = UpdateTemplate.builder()
                .messageId(messageId)
                .status(actionType)
                .i18nResources(
                        I18nResources.builder()
                                .locale("zh-CN")
                                .isDefault(true)
                                .texts(new HashMap<>())
                                .build()
                )
                .build();
        System.out.println(JSON.toJSONString(template));
        return JSON.toJSONString(template);
    }


    /**
     * 获取飞书token
     *
     * @return
     * @throws IOException
     * @throws InterruptedException
     */
    @Nullable
    private String getAccessToken(String tenantKey) throws IOException, InterruptedException {
        FeiShuConfigProperties.FeiShuAppConfig config = feishuConfigStrategy.getConfigByTenantKey(tenantKey);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("app_id", config.getAppId());
        jsonObject.put("app_secret", config.getAppSecret());
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(JSON.toJSONString(jsonObject)))
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        JSONObject accessTokenResponse = JSON.parseObject(response.body());
        if (accessTokenResponse.containsKey("tenant_access_token")) {
            return accessTokenResponse.getString("tenant_access_token");
        }
        return null;
    }


    /**
     * 获取时间戳
     *
     * @param date
     * @return
     */
    public String getTimestamp(Date date) {
        return date == null ? String.valueOf(System.currentTimeMillis()) : String.valueOf(date.getTime());
    }

    /**
     * 获取当前用户openId
     *
     * @return
     */
    public List<String> openId() {
        UserInfo currentUser = AuthUtil.getCurrentUser();
        Long userId = currentUser.getId();
        List<Long> thirdUserIds = thirdUserAuthService.list(Wrappers.<ThirdUserAuth>lambdaQuery()
                        .eq(ThirdUserAuth::getUserId, userId)
                        .select(ThirdUserAuth::getThirdUserId))
                .stream().map(ThirdUserAuth::getThirdUserId).toList();
        if (thirdUserIds.isEmpty()) {
            return null;
        }
        return thirdUserService.list(Wrappers.<ThirdUser>lambdaQuery()
                        .in(ThirdUser::getId, thirdUserIds)
                        .select(ThirdUser::getOpenId))
                .stream().map(ThirdUser::getOpenId).toList();
    }


    private String getOpenId(String userId, String tenantKey) {
        List<Long> thirdUserIds = thirdUserAuthService.list(Wrappers.<ThirdUserAuth>lambdaQuery()
                        .eq(ThirdUserAuth::getUserId, userId)
                        .select(ThirdUserAuth::getThirdUserId))
                .stream().map(ThirdUserAuth::getThirdUserId).toList();
        if (CollectionUtils.isEmpty(thirdUserIds)) {
            return null;
        }
        return thirdUserService.list(Wrappers.<ThirdUser>lambdaQuery()
                        .in(ThirdUser::getId, thirdUserIds)
                        .eq(ThirdUser::getTenantKey, tenantKey)
                        .select(ThirdUser::getOpenId))
                .stream().map(ThirdUser::getOpenId).findFirst().orElse(null);
    }
}
